/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim: sw=2 ts=2 sts=2 expandtab * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */#include"StorageBaseStatementInternal.h"#include"nsProxyRelease.h"#include"mozStorageBindingParamsArray.h"#include"mozStorageStatementData.h"#include"mozStorageAsyncStatementExecution.h"namespacemozilla{namespacestorage{//////////////////////////////////////////////////////////////////////////////////// Local Classes/** * Used to finalize an asynchronous statement on the background thread. */classAsyncStatementFinalizer:publicRunnable{public:/** * Constructor for the event. * * @param aStatement * We need the AsyncStatement to be able to get at the sqlite3_stmt; * we only access/create it on the async thread. * @param aConnection * We need the connection to know what thread to release the statement * on. We release the statement on that thread since releasing the * statement might end up releasing the connection too. */AsyncStatementFinalizer(StorageBaseStatementInternal*aStatement,Connection*aConnection):Runnable("storage::AsyncStatementFinalizer"),mStatement(aStatement),mConnection(aConnection){}NS_IMETHODRun()override{if(mStatement->mAsyncStatement){sqlite3_finalize(mStatement->mAsyncStatement);mStatement->mAsyncStatement=nullptr;}nsCOMPtr<nsIThread>targetThread(mConnection->threadOpenedOn);NS_ProxyRelease("AsyncStatementFinalizer::mStatement",targetThread,mStatement.forget());returnNS_OK;}private:RefPtr<StorageBaseStatementInternal>mStatement;RefPtr<Connection>mConnection;};/** * Finalize a sqlite3_stmt on the background thread for a statement whose * destructor was invoked and the statement was non-null. */classLastDitchSqliteStatementFinalizer:publicRunnable{public:/** * Event constructor. * * @param aConnection * Used to keep the connection alive. If we failed to do this, it * is possible that the statement going out of scope invoking us * might have the last reference to the connection and so trigger * an attempt to close the connection which is doomed to fail * (because the asynchronous execution thread must exist which will * trigger the failure case). * @param aStatement * The sqlite3_stmt to finalize. This object takes ownership / * responsibility for the instance and all other references to it * should be forgotten. */LastDitchSqliteStatementFinalizer(RefPtr<Connection>&aConnection,sqlite3_stmt*aStatement):Runnable("storage::LastDitchSqliteStatementFinalizer"),mConnection(aConnection),mAsyncStatement(aStatement){NS_PRECONDITION(aConnection,"You must provide a Connection");}NS_IMETHODRun()override{(void)::sqlite3_finalize(mAsyncStatement);mAsyncStatement=nullptr;nsCOMPtr<nsIThread>target(mConnection->threadOpenedOn);(void)::NS_ProxyRelease("LastDitchSqliteStatementFinalizer::mConnection",target,mConnection.forget());returnNS_OK;}private:RefPtr<Connection>mConnection;sqlite3_stmt*mAsyncStatement;};//////////////////////////////////////////////////////////////////////////////////// StorageBaseStatementInternalStorageBaseStatementInternal::StorageBaseStatementInternal():mAsyncStatement(nullptr){}voidStorageBaseStatementInternal::asyncFinalize(){nsIEventTarget*target=mDBConnection->getAsyncExecutionTarget();if(target){// Attempt to finalize asynchronouslynsCOMPtr<nsIRunnable>event=newAsyncStatementFinalizer(this,mDBConnection);// Dispatch. Note that dispatching can fail, typically if// we have a race condition with asyncClose(). It's ok,// let asyncClose() win.(void)target->Dispatch(event,NS_DISPATCH_NORMAL);}// If we cannot get the background thread,// mozStorageConnection::AsyncClose() has already been called and// the statement either has been or will be cleaned up by// internalClose().}voidStorageBaseStatementInternal::destructorAsyncFinalize(){if(!mAsyncStatement)return;boolisOwningThread=false;(void)mDBConnection->threadOpenedOn->IsOnCurrentThread(&isOwningThread);if(isOwningThread){// If we are the owning thread (currently that means we're also the// main thread), then we can get the async target and just dispatch// to it.nsIEventTarget*target=mDBConnection->getAsyncExecutionTarget();if(target){nsCOMPtr<nsIRunnable>event=newLastDitchSqliteStatementFinalizer(mDBConnection,mAsyncStatement);(void)target->Dispatch(event,NS_DISPATCH_NORMAL);}}else{// If we're not the owning thread, assume we're the async thread, and// just run the statement.nsCOMPtr<nsIRunnable>event=newLastDitchSqliteStatementFinalizer(mDBConnection,mAsyncStatement);(void)event->Run();}// We might not be able to dispatch to the background thread,// presumably because it is being shutdown. Since said shutdown will// finalize the statement, we just need to clean-up around here.mAsyncStatement=nullptr;}NS_IMETHODIMPStorageBaseStatementInternal::NewBindingParamsArray(mozIStorageBindingParamsArray**_array){nsCOMPtr<mozIStorageBindingParamsArray>array=newBindingParamsArray(this);NS_ENSURE_TRUE(array,NS_ERROR_OUT_OF_MEMORY);array.forget(_array);returnNS_OK;}NS_IMETHODIMPStorageBaseStatementInternal::ExecuteAsync(mozIStorageStatementCallback*aCallback,mozIStoragePendingStatement**_stmt){// We used to call Connection::ExecuteAsync but it takes a// mozIStorageBaseStatement signature because it is also a public API. Since// our 'this' has no static concept of mozIStorageBaseStatement and Connection// would just QI it back across to a StorageBaseStatementInternal and the// actual logic is very simple, we now roll our own.nsTArray<StatementData>stmts(1);StatementDatadata;nsresultrv=getAsynchronousStatementData(data);NS_ENSURE_SUCCESS(rv,rv);NS_ENSURE_TRUE(stmts.AppendElement(data),NS_ERROR_OUT_OF_MEMORY);// Dispatch to the backgroundreturnAsyncExecuteStatements::execute(stmts,mDBConnection,mNativeConnection,aCallback,_stmt);}NS_IMETHODIMPStorageBaseStatementInternal::EscapeStringForLIKE(constnsAString&aValue,constchar16_taEscapeChar,nsAString&_escapedString){constchar16_tMATCH_ALL('%');constchar16_tMATCH_ONE('_');_escapedString.Truncate(0);for(uint32_ti=0;i<aValue.Length();i++){if(aValue[i]==aEscapeChar||aValue[i]==MATCH_ALL||aValue[i]==MATCH_ONE){_escapedString+=aEscapeChar;}_escapedString+=aValue[i];}returnNS_OK;}}// namespace storage}// namespace mozilla